package vip.wangwenhao.autoconfigure.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import vip.wangwenhao.autoconfigure.service.CacheKeyGenerator;
import vip.wangwenhao.common.annotations.Lock;
import vip.wangwenhao.common.annotations.LockParam;
import vip.wangwenhao.common.constants.CommonConstants;
import vip.wangwenhao.common.constants.SymbolConstants;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

/**
 * @author wangwenhao
 */
@Service
@Slf4j
public class LockKeyGenerator implements CacheKeyGenerator {

    @Override
    public String getLockKey(ProceedingJoinPoint pjp) {
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        Lock lock = AnnotatedElementUtils.getMergedAnnotation(method, Lock.class);
        String delimiter = null;
        String key = null;
        if (null != lock) {
            delimiter = lock.delimiter();
            String lockKey = lock.key();
            if (!StringUtils.isEmpty(lockKey)) {
                key = lockKey;
            }
        }
        if (StringUtils.isEmpty(delimiter)) {
            delimiter = SymbolConstants.COLON;
        }
        if (StringUtils.isEmpty(key)) {
            String serviceName = pjp.getTarget().getClass().getName();
            String methodName = method.getName();
            key = serviceName + delimiter + methodName;
        }

        final Object[] args = pjp.getArgs();
        final Parameter[] parameters = method.getParameters();
        StringBuilder builder = new StringBuilder();
        // 默认解析方法里面带 LockParam 注解的属性,如果没有尝试着解析实体对象中的
        for (int i = CommonConstants.ZERO; i < parameters.length; i++) {
            final LockParam annotation = AnnotatedElementUtils.getMergedAnnotation(parameters[i], LockParam.class);
            if (annotation == null) {
                continue;
            }
            String name = annotation.name();
            if (StringUtils.isEmpty(name)) {
                name = parameters[i].getName();
            }
            builder.append(delimiter).append(name).append(delimiter).append(JSONObject.toJSONString(args[i], SerializerFeature.IgnoreNonFieldGetter));
        }
        final Annotation[][] parameterAnnotations = method.getParameterAnnotations();
        for (int i = CommonConstants.ZERO; i < parameterAnnotations.length; i++) {
            final Object object = args[i];
            final Field[] fields = object.getClass().getDeclaredFields();
            for (Field field : fields) {
                final LockParam annotation = AnnotatedElementUtils.getMergedAnnotation(field, LockParam.class);
                if (annotation == null) {
                    continue;
                }
                field.setAccessible(true);
                String name = annotation.name();
                if (StringUtils.isEmpty(name)) {
                    name = field.getName();
                }
                builder.append(delimiter).append(name).append(delimiter).append(JSONObject.toJSONString(ReflectionUtils.getField(field, object), SerializerFeature.IgnoreNonFieldGetter));
            }
        }
        log.info("key:{}", key + builder.toString());
        return key + builder.toString();
    }

}